home *** CD-ROM | disk | FTP | other *** search
/ Precision Software Appli…tions Silver Collection 1 / Precision Software Applications Silver Collection Volume One (PSM) (1993).iso / tutor / asm1tut.exe / CHAP12.DOC < prev    next >
Text File  |  1990-06-25  |  18KB  |  443 lines

  1.  
  2.  
  3.  
  4.                                                                            122
  5.  
  6.  
  7.                           CHAPTER 12 - MULTIPLE WORD ARITHMETIC I
  8.  
  9.  
  10.              Let's review the LOOP instruction. We often want to repeat an
  11.              action a specific number of times. In a FOR loop, we write:
  12.  
  13.                  FOR I = 1, 10
  14.  
  15.              That means we want to repeat the code that follows ten times. The
  16.              8086 has an instruction for this, called the loop instruction. It
  17.              looks like this:
  18.  
  19.                  mov  cx, 10
  20.              label17:
  21.                  ...
  22.                  (a bunch of code)
  23.                  ...
  24.                  loop label17
  25.  
  26.              The count MUST be in the CX register. This is the only register
  27.              you can use for this. When the machine sees the loop instruction,
  28.              it decrements the CX register by one, LEAVING ALL FLAGS ALONE,
  29.              and if the result is not zero, it loops back to the label. If the
  30.              result is zero, it falls through the loop. One problem we might
  31.              have with this instruction is if you enter it with CX = 0, it is
  32.              going to loop 65,536 times. Intel provided a second instruction
  33.              to avoid this - JCXZ (jump if CX is zero). You put it right
  34.              before the loop for insurance.
  35.  
  36.                  jcxz label19
  37.              label17:
  38.                  ...
  39.                  (a bunch of code)
  40.                  ...
  41.                  loop label17
  42.              label19:
  43.  
  44.              Obviously, in our first example this instruction is not necessary
  45.              because we set CX to 10 just before entering the loop.
  46.  
  47.  
  48.  
  49.              If you have seen the list of 8086 instructions, you will have
  50.              noticed lots of strange looking add and subtract instructions.
  51.              Why are they there? In this chapter we will look at ADC and SBB.
  52.              The others will come in later chapters. 
  53.  
  54.              How do engineers decide what a reasonable size for a number is?
  55.              When they started making 8 bit machines (the maximum unsigned
  56.              number is 255) did they go out on the streets and take a poll to
  57.              find out if 255 was the maximum number that people used?  No,
  58.              they didn't even think about what people needed. It was a
  59.              question of what the technology would allow at that time.
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              Chapter 12 - Multiple Word Arithmetic I                       123
  69.              _______________________________________
  70.  
  71.              Similarly, at the time the 8086 was engineered, 16 bits was
  72.              pushing the limits of the technology. But 65,535 doesn't really
  73.              cut the mustard. If those are 65,535 pennies, that isn't even
  74.              your yearly food bill, let alone the cost of housing.
  75.  
  76.              The 8086 instructions give us the option of making integers of
  77.              whatever size we want. Because it is done in software, it is
  78.              slower, but if we are doing thousands or tens of thousands of
  79.              additions instead of hundreds of thousands or millions, it won't
  80.              be a great inconvenience.{1} 
  81.  
  82.              ASMHELP.OBJ is set up to input 2 word (4 byte) and 4 word (8
  83.              byte) numbers so those are what we are going to use. 4 byte
  84.              numbers are up to +/- 2 billion and 8 byte numbers are up to
  85.              9X10exp18. Those should be large enough. 
  86.  
  87.              The first instruction is ADC, add with carry. When you add by
  88.              hand, you add everything in the right column, then carry to the
  89.              next column left, repeating this over and over. We don't need to
  90.              do it column by column, but it is necessary to do it word by
  91.              word. In the data section we need:
  92.  
  93.              variable1  dq  ?
  94.              variable2  dq  ?
  95.  
  96.              and the code for a 4 word (8 byte) addition is the following:
  97.  
  98.                  lea  si, variable1
  99.                  lea  di, variable2
  100.                  mov  ax, [di]                 ; first addition
  101.                  add  [si], ax
  102.                  pushf                    ; save the flags
  103.                  mov  cx, 3 
  104.                  add  si, 2               ; go to next higher word
  105.                  add  di, 2
  106.  
  107.              long_add_loop:               ; next three additions
  108.                  mov  ax, [di]
  109.                  popf                     ; restore the flags
  110.                  adc  [si], ax
  111.                  pushf                    ; save the new flags
  112.                  add  di, 2
  113.                  add  si, 2
  114.                  loop long_add_loop
  115.  
  116.                  popf                     ; pop the flags off the stack
  117.  
  118.  
  119.              ADC is the same as ADD, but it looks at the carry flag - if the
  120.              carry flag is 1, it adds 1 to the result; if the carry flag is
  121.              zero, it does nothing to the result. This works for both signed
  122.              and unsigned numbers. If you don't believe it, you should go back
  123.              ____________________
  124.  
  125.                 1 As a benchmark, it took a moderately slow PC 6.5 seconds to
  126.              do 100,000 eight byte additions. The same PC can do over a
  127.              million two byte additions in 6 seconds.
  128.  
  129.  
  130.  
  131.  
  132.              The PC Assembler Tutor                                        124
  133.              ______________________
  134.  
  135.              to the introductory chapter with the base 10 machine and look at
  136.              long additions.
  137.  
  138.              Notice PUSHF and POPF. These are special instructions called push
  139.              flags and pop flags. Rather than pushing an arithmetic register
  140.              on the stack, pushf pushes the register containing the flags.
  141.              Popf pops them back into the flags register. This is necessary
  142.              because ADC looks at the carry flag and the ADD instructions in
  143.              the loop:
  144.  
  145.                  add  di,2
  146.                  add  si,2
  147.  
  148.              might change the carry flag. The last POPF after the loop is to
  149.              get it off the stack (anything we put on the stack we need to
  150.              take off the stack).
  151.  
  152.              The first addition is a normal addition, the last three take the
  153.              carry into account. We are moving the pointers a word at a time.
  154.              Because the 8086 doesn't allow both operands to be in memory, we
  155.              need to move one into a register. After the addition is
  156.              performed, the result is in memory. We can discard what is in the
  157.              register. 
  158.  
  159.              Notice that the first half of the code looks almost the same as
  160.              the code inside the loop. If we could only use ADC instead of
  161.              ADD, we could put the first addition inside the loop. It is
  162.              possible to do this. There is another instruction, CLC, which
  163.              clears the carry flag. Recall that if the carry flag is 0, ADC
  164.              does nothing different from ADD. Therefore, we can have:
  165.  
  166.                  lea  si, variable1
  167.                  lea  di, variable2
  168.                  mov  cx, 4               ; 4 additions in loop
  169.                  clc                      ; set cf to zero
  170.                  pushf                    ; save the flags
  171.  
  172.              long_add_loop:
  173.                  mov  ax, [di]            ; word to a register
  174.                  popf                     ; restore the flags
  175.                  adc  [si], ax            ; register + memory
  176.                  pushf                    ; save the flags
  177.                  add  di, 2
  178.                  add  si, 2
  179.                  loop long_add_loop
  180.  
  181.                  popf                     ; pop the flags off the stack
  182.  
  183.              It's the same code. The number of loops was increased from 3 to
  184.              4, and the carry flag was cleared to insure that the first
  185.              addition would have nothing extra added. Here is the basic
  186.              program.
  187.  
  188.              ; - - - - - PUT DATA BELOW THIS LINE
  189.              variable1  dq  ?
  190.              variable2  dq  ?
  191.              ; - - - - - PUT DATA ABOVE THIS LINE
  192.  
  193.  
  194.  
  195.  
  196.              Chapter 12 - Multiple Word Arithmetic I                       125
  197.              _______________________________________
  198.  
  199.  
  200.              ; - - - - - PUT CODE BELOW THIS LINE
  201.  
  202.                  call  show_regs
  203.  
  204.              outer_loop:
  205.                  lea  ax, variable1            ; get the variables
  206.                  call get_signed_8byte
  207.                  call print_signed_8byte
  208.                  lea  ax, variable2
  209.                  call get_signed_8byte
  210.                  call print_signed_8byte
  211.  
  212.                  lea  si, variable1            ; set the pointers
  213.                  lea  di, variable2
  214.                  mov  cx, 4                    ; loop 4 times (4 words)
  215.                  clc                           ; clear the cf
  216.                  pushf                         ; save the flags
  217.  
  218.              long_add_loop:
  219.                  mov  ax, [di]            ; word to a register
  220.                  popf                     ; restore the flags
  221.                  adc  [si], ax            ; register + memory
  222.                  pushf                    ; save the flags
  223.                  add  di, 2
  224.                  add  si, 2
  225.                  loop long_add_loop
  226.  
  227.                  popf                     ; restore the flags
  228.  
  229.                  lea  ax, variable1
  230.                  call print_signed_8byte
  231.  
  232.                  jmp  outer_loop
  233.  
  234.              ; - - - - - PUT CODE ABOVE THIS LINE
  235.  
  236.              The calls to get_signed_8byte are followed by print_signed_8byte.
  237.              This is so you can see what you have actually typed in. It will
  238.              be neat and with commas, so it will be much easier to read.
  239.              Everything else is the same as before.
  240.  
  241.              As an aside, let's talk about commas. Though we can get along
  242.              without commas if we have a 4 digit number, There is no reason to
  243.              do without them when using larger numbers. I find printer output
  244.              that has 15 digit floating point numbers without commas not only
  245.              hard to read but also silly. It's not that the computer is
  246.              incapable of putting in commas, it's that the prople who wrote
  247.              the programs couldn't be bothered with doing 2 hours of work to
  248.              save the users lots and lots of aggrivation. Therefore, for all
  249.              large number input (that is, larger than 1 word)  you can use
  250.              commas. They don't even have to be in the right place, the
  251.              computer ignores them. All the following are the same number:
  252.  
  253.                  723469746
  254.                  723,4,69746
  255.                  72,3,46974,6
  256.  
  257.  
  258.  
  259.  
  260.              The PC Assembler Tutor                                        126
  261.              ______________________
  262.  
  263.                  7,2,3,4,6,9,7,4,6
  264.                  723,469,746
  265.  
  266.              The program strips all commas out of the line and then looks at
  267.              the input. All large output has the commas in the right spot. In
  268.              order to enlist you in the crusade to stamp out unreadable input
  269.              and output, the summary at the end of this chapter has the C code
  270.              necessary for stripping commas. This is my contribution to world
  271.              culture.
  272.  
  273.              If you have played with the signed addition program, all we need
  274.              to do to make it unsigned is to change all the get_signed_8byte
  275.              calls to get_unsigned_8byte calls. Change the print calls to
  276.              unsigned also. Run the program.
  277.  
  278.              Now, let's try subtraction. How much code do we have to alter to
  279.              make it a subtraction program? The answer is - one word. Just
  280.              change the ADC (add with carry) to SBB (subtract with borrow).
  281.              SBB learns from the CF flag whether the last subtraction had to
  282.              borrow, and tells the next subtraction via the CF flag whether it
  283.              has had to borrow. Change it, and try it out. (Yes, really do it,
  284.              don't just pretend that you are going to do it). 
  285.  
  286.              Why does this program work with exactly 8 bytes? Because we tell
  287.              the loop via CX that it is 4 words long. If we put 2 in CX, the
  288.              loop will think that the number is 2 words (4 bytes) long, and if
  289.              we put 17 in CX, the loop will think that the number is 17 words
  290.              (34 bytes) long. In fact, this little snippet of code can do
  291.              either signed or unsigned addition or subtraction of any number
  292.              of words simply by altering one number and one word of code. 
  293.  
  294.              The code as it stands has only one shortfall. Remember from our
  295.              earlier subtraction that we might want to have an INTO (interrupt
  296.              on overflow) instruction after signed addition and subtraction.
  297.              It needs to be after the last addition (or subtraction). All we
  298.              need to do is put it after the POPF just below the loop. At this
  299.              point the flags show the condition right after the last addition
  300.              or subtraction:
  301.  
  302.                  loop long_add_loop
  303.  
  304.                  popf                ; pop the flags off the stack
  305.                  into                ; interrupt if overflow is set
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.  
  317.  
  318.  
  319.  
  320.  
  321.  
  322.  
  323.              Chapter 12 - Multiple Word Arithmetic I                       127
  324.              _______________________________________
  325.  
  326.                                        SUMMARY
  327.  
  328.  
  329.              ADC (add with carry) is used for multiple word arithmetic. It
  330.              adds two numbers along with plus the value in CF, the carry flag
  331.              (1 if the flag is set and 0 if the flag is cleared). CLC (clear
  332.              the carry flag) should be used to clear CF before the first
  333.              addition. After the addition, the flags register should be pushed
  334.              in order to store CF unless it is certain that CF will not be
  335.              effected by the rest of the loop.
  336.  
  337.                  popf
  338.                  adc  [di], ax
  339.                  pushf
  340.  
  341.  
  342.  
  343.              SBB (subtract with borrow) is used for multiple word arithmetic.
  344.              It subtracts one number from the other and subtracts the value in
  345.              CF to account for any borrows from the right. CLC (clear the
  346.              carry flag) should be used to clear CF before the first
  347.              subtraction. After the subtraction, the flags register should be
  348.              pushed in order to store CF unless it is certain that CF will not
  349.              be effected by the rest of the loop.
  350.  
  351.                  popf
  352.                  sbb  [di], ax
  353.                  pushf
  354.  
  355.              These operations have the typical 5 possibilities:
  356.  
  357.                  1) register, register
  358.                  2) register, memory
  359.                  3) memory, register
  360.                  4) memory, constant
  361.                  5) register, constant
  362.  
  363.  
  364.  
  365.              You may manually control the value in CF with STC and CLC. STC
  366.              (set the carry flag) sets the value to 1, while CLC (clear the
  367.              carry flag) sets the value to 0. These are used for initial
  368.              settings of multiple word operations.
  369.  
  370.  
  371.              In order to store the values in the flags register you use PUSHF
  372.              (push the flags) until you need them again. At that time you can
  373.              get them back with POPF (pop the flags).
  374.  
  375.  
  376.  
  377.  
  378.  
  379.  
  380.  
  381.  
  382.  
  383.  
  384.  
  385.  
  386.  
  387.              The PC Assembler Tutor                                        128
  388.              ______________________
  389.  
  390.                              STRIPPING COMMAS IN C
  391.  
  392.              In order to get rid of commas, you need some discipline in what
  393.              kind of data entry you have. Specifically, you can't enter large
  394.              numbers on the same line as text strings because text strings are
  395.              likely to have commas that should be kept. This is hardly a major
  396.              restriction. You can have as many numbers as you want on the same
  397.              line since in C you must have whitespace between pieces of data.
  398.  
  399.              We will take all commas out of the line. You can retrofit most
  400.              old programs with almost no change. 
  401.  
  402.              The only time that you need this capability is if you are getting
  403.              something from the keyboard, and what you probably have in the
  404.              program is:
  405.  
  406.                  scanf ( "format string", variables );
  407.  
  408.              The method is (1) import a text string as a single string, (2)
  409.              strip the commas, and (3) use sscanf instead of scanf.
  410.  
  411.                  char buffer[80] ;
  412.  
  413.                  fgets (buffer, 80, stdin) ;
  414.                  strip_commas (buffer);
  415.                  sscanf (buffer, "format string", variables) ;
  416.  
  417.              Both "format string" and the variables remain unchanged when you
  418.              switch from scanf to sscanf. You might want to check for EOF with
  419.              fgets.
  420.  
  421.              Heres the program:
  422.  
  423.              strip_commas (buffer)
  424.              char   *buffer ;
  425.              {
  426.                  char *ptr1, *ptr2 ;
  427.  
  428.                  ptr1 = ptr2 = buffer ;
  429.                  while (1)
  430.                  {
  431.                       if ( *ptr2 == ',')  /* skip commas */
  432.                       {
  433.                            ptr2++ ;
  434.                            continue ;
  435.                       }
  436.                       /*move, increment, and check for 0 */
  437.                       if (!(*ptr1++ = *ptr2++))   /* this is '=', not '==' */
  438.                            break ;
  439.                  }
  440.                  return ;
  441.              }
  442.  
  443.